package com.wei.czz.framework.common.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.handler.WriteHandler;
import com.wei.czz.common.annotation.Auth;
import com.wei.czz.common.annotation.Resubmit;
import com.wei.czz.common.dto.common.NameValue;
import com.wei.czz.common.dto.common.UploadDto;
import com.wei.czz.common.dto.common.multiDict.MultiDictDto;
import com.wei.czz.common.dto.common.multiDict.MultiDictPageEnumDto;
import com.wei.czz.common.easyexcel.handler.CzzWriteHandler;
import com.wei.czz.common.easyexcel.listener.ObjectUploadListener;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.common.multiDIct.MultiDictEnum;
import com.wei.czz.common.jsr303.group.Save;
import com.wei.czz.common.jsr303.group.Search;
import com.wei.czz.common.jsr303.group.Update;
import com.wei.czz.common.property.BatchProperty;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.tool.Result;
import com.wei.czz.common.utils.CopyUtils;
import com.wei.czz.common.utils.ExcelUtils;
import com.wei.czz.common.vo.common.UpdateStatusVo;
import com.wei.czz.common.vo.common.multiDict.MultiDictFormVo;
import com.wei.czz.common.vo.common.multiDict.MultiDictUploadVo;
import com.wei.czz.common.vo.common.multiDict.MultiDictVo;
import com.wei.czz.framework.common.manager.MultiDictManager;
import com.wei.czz.framework.common.service.MultiDictService;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2023-12-26 11:50:17
 * className: MultiDictController
 * version: 1.0
 * description:
 */
@RestController
@RequestMapping("/multi-dict")
@AllArgsConstructor
public class MultiDictController {

    private static final Logger log = LoggerFactory.getLogger(MultiDictController.class);

    private final BatchProperty batchProperty;

    private final MultiDictManager multiDictManager;

    private final MultiDictService multiDictService;

    /**
     * 保存级联字典接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-级联字典管理页面-字典操作弹出框调用
     * <p>
     * 此接口会校验是否重复提交
     * @param multiDictVo 参数对象
     * @return 结果
     */
    @Resubmit(spEL = "'saveMultiDict' + #userPo.userId", message = "保存级联字典")
    @PostMapping("/save")
    @Auth(permit = "multi:dict:save")
    public Result<Object> saveMultiDict(@Validated(Save.class) @RequestBody MultiDictVo multiDictVo) {
        log.info("开始保存级联字典业务。{}", multiDictVo);

        multiDictManager.saveMultiDict(multiDictVo);

        log.info("保存级联字典业务结束");
        return Result.success();
    }

    /**
     * 上传级联字典接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-级联字典管理页面-上传抽屉中调用
     * <p>
     * 此接口会校验是否重复提交
     * @param file    文件对象
     * @param mustAll 是否必须全部成功
     * @return 结果
     */
    @Resubmit(spEL = "'uploadMultiDict' + #userId", message = "上传级联字典")
    @PostMapping("/upload")
    @Auth(permit = "multi:dict:save")
    public Result<UploadDto<MultiDictUploadVo>> uploadMultiDict(@RequestParam MultipartFile file,
                                                                @RequestParam("mustAll") Boolean mustAll) {
        log.info("开始上传级联字典业务。fileName={} fileSize={} mustAll={}", file.getOriginalFilename(), file.getSize(), mustAll);

        ObjectUploadListener<MultiDictUploadVo> objectUploadListener = new ObjectUploadListener<>();
        try {
            // 解析数据
            EasyExcel.read(file.getInputStream(), MultiDictUploadVo.class, objectUploadListener).sheet().doRead();

        } catch (IOException e) {
            log.error("级联字典文件解析异常。message={}", e.getMessage(), e);
            return Result.fail();
        }

        if (objectUploadListener.isEmpty()) {
            log.info("级联字典文件内容为空");
            return Result.clientFail("级联字典文件内容不能为空");
        }
        if (batchProperty.getMaxSize() < objectUploadListener.getTotal()) {
            log.warn("级联字典导入数量超限。batch.maxSize={} list.size={}", batchProperty.getMaxSize(),
                    objectUploadListener.getTotal());
            return Result.clientFail("一次导入级联字典操作最多允许同时导入" + batchProperty.getMaxSize() + "个字典");
        }

        UploadDto<MultiDictUploadVo> uploadDto = multiDictManager.uploadMultiDict(objectUploadListener, mustAll);

        log.info("上传级联字典业务结束");
        return Result.success(uploadDto);
    }

    /**
     * 分页获取级联字典接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-级联字典管理页面调用
     * <p>
     * 此接口会校验是否重复提交
     * @param multiDictFormVo 参数对象
     * @return 结果
     */
    @PostMapping("/page/list")
    @Auth(permit = "multi:dict:list")
    public Result<PageDto<MultiDictDto>> getMultiDictPageList(
            @Validated(Search.class) @RequestBody MultiDictFormVo multiDictFormVo) {
        log.info("开始分页查询级联字典业务。{}", multiDictFormVo);

        PageDto<MultiDictDto> pageDto = multiDictManager.getMultiDictPageList(multiDictFormVo);

        log.info("分页查询级联字典业务结束");
        return Result.success(pageDto);
    }

    /**
     * 获取级联字典管理页面参数接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-级联字典管理页面调用
     * @return 结果
     */
    @GetMapping("/page/param")
    @Auth(permit = "multi:dict:list")
    public Result<MultiDictPageEnumDto> getMultiDictPageParam() {
        log.info("开始获取级联字典管理页面参数业务");

        MultiDictPageEnumDto multiDictPageEnumDto = multiDictManager.getMultiDictPageParam();

        log.info("获取级联字典管理页面参数业务结束");
        return Result.success(multiDictPageEnumDto);
    }

    /**
     * 获取级联字典列表接口
     * 获取类型关联的所有级联字典
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-级联字典管理页面调用
     * @param type 类型
     * @return 结果
     */
    @GetMapping("/type-list")
    @Auth(permit = "multi:dict:list")
    public Result<List<MultiDictDto>> getMultiDictList(@RequestParam("type") Integer type) {
        log.info("开始获取级联字典列表业务。type={}", type);

        List<MultiDictDto> list = multiDictManager.getMultiDictList(type);

        log.info("获取级联字典列表业务结束");
        return Result.success(list);
    }

    /**
     * 获取级联字典树接口
     * 获取类型关联的所有级联字典
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-级联字典管理页面-新增/修改级联字典弹出框调用
     * @param type 类型
     * @param id   主键
     * @return 结果
     */
    @GetMapping("/type-tree")
    @Auth(permit = { "multi:dict:save", "multi:dict:update" })
    public Result<MultiDictDto> getMultiDictTree(@RequestParam("type") Integer type,
                                                       @RequestParam(value = "id", required = false) Long id) {
        log.info("开始获取级联字典树业务。type={} id={}", type, id);

        MultiDictDto multiDictDto = multiDictManager.getMultiDictTree(type, id);

        log.info("获取级联字典树业务结束");
        return Result.success(multiDictDto);
    }

    /**
     * 获取下一个排序等级接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-级联字典管理页面-新增/修改级联字典弹出框调用
     * @param parentId 父级主键
     * @param type     类型
     * @return 结果
     */
    @GetMapping("/next/sort")
    @Auth(permit = { "multi:dict:save", "multi:dict:update" })
    public Result<Integer> getNextSort(@RequestParam("parentId") Long parentId,
                                       @RequestParam("type") Integer type) {
        log.info("开始获取下一个排序等级业务。parentId={} type={}", parentId, type);

        Integer sort = multiDictService.getNextSort(parentId, type);

        log.info("获取下一个排序等级业务结束");
        return Result.success(sort);
    }

    /**
     * 下载级联字典导入模板接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-级联字典管理页面调用
     * @return 结果
     */
    @GetMapping("/download/upload-template")
    @Auth(permit = "multi:dict:save")
    public Result<Object> downloadUploadTemplate(HttpServletResponse response) {
        log.info("开始下载级联字典导入模板业务");



        List<MultiDictUploadVo> list = new ArrayList<>();

        MultiDictUploadVo multiDictUploadVo = new MultiDictUploadVo();
        multiDictUploadVo.setSerial(1);
        multiDictUploadVo.setParentNo(CommonEnum.ZERO.getLongValue());
        multiDictUploadVo.setTypeName(MultiDictEnum.STORE_CLASS.getName());
        multiDictUploadVo.setName("第一层级‘商店分类’，父级‘商店分类’主键为0");
        multiDictUploadVo.setValue("test_value");
        multiDictUploadVo.setSort(1);
        multiDictUploadVo.setStatusName("可用");
        multiDictUploadVo.setDefaultStatusName("否");
        list.add(multiDictUploadVo);

        multiDictUploadVo = new MultiDictUploadVo();
        multiDictUploadVo.setSerial(2);
        multiDictUploadVo.setParentNo(1L);
        multiDictUploadVo.setTypeName(MultiDictEnum.STORE_CLASS.getName());
        multiDictUploadVo.setName("第二层级‘商店分类’，父级‘商店分类’为同一批导入的序号1");
        multiDictUploadVo.setValue("value2");
        multiDictUploadVo.setSort(1);
        multiDictUploadVo.setStatusName("禁用");
        multiDictUploadVo.setDefaultStatusName("否");
        list.add(multiDictUploadVo);

        multiDictUploadVo = new MultiDictUploadVo();
        multiDictUploadVo.setSerial(3);
        multiDictUploadVo.setParentNo(1234567890L);
        multiDictUploadVo.setTypeName(MultiDictEnum.STORE_CLASS.getName());
        multiDictUploadVo.setName("父级‘商店分类’为主键1234567890");
        multiDictUploadVo.setValue("value3");
        multiDictUploadVo.setSort(1);
        multiDictUploadVo.setStatusName("可用");
        multiDictUploadVo.setDefaultStatusName("是");
        list.add(multiDictUploadVo);


        List<String> typeNameList = CopyUtils.mapList(MultiDictEnum.getList(), NameValue::getName);
        List<String> statusNameList = Arrays.asList("可用", "禁用");
        List<String> defaultStatusNameList = Arrays.asList("是", "否");

        // 设置数据列的枚举列表映射
        Map<Integer, List<String>> enumMap = new HashMap<>();
        enumMap.put(2, typeNameList);
        enumMap.put(6, statusNameList);
        enumMap.put(7, defaultStatusNameList);

        CzzWriteHandler czzWriteHandler = new CzzWriteHandler(500, enumMap);

        List<WriteHandler> writeHandlerList = ExcelUtils.basicWriteHandlerList();
        writeHandlerList.add(czzWriteHandler);

        ExcelUtils.writeResponse("级联字典导入模板", list, multiDictUploadVo.getTemplatePropertyList(),
                MultiDictUploadVo.class, writeHandlerList, response);

        log.info("下载级联字典导入模板业务结束");
        return null;
    }

    /**
     * 修改级联字典接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-级联字典管理页面-新增/修改级联字典弹出框调用
     * <p>
     * 此接口会校验是否重复提交
     * @param multiDictVo 参数对象
     * @return 结果
     */
    @Resubmit(spEL = "'updateMultiDict' + #userPo.userId", message = "修改级联字典")
    @PostMapping("/update")
    @Auth(permit = "multi:dict:update")
    public Result<Object> updateMultiDict(@Validated(Update.class) @RequestBody MultiDictVo multiDictVo) {
        log.info("开始修改级联字典业务。{}", multiDictVo);

        multiDictManager.updateMultiDict(multiDictVo);

        log.info("修改级联字典业务结束");
        return Result.success();
    }

    /**
     * 修改级联字典状态接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-级联字典管理页面调用
     * <p>
     * 此接口会校验是否重复提交
     * @param updateStatusVo 参数对象
     * @return 结果
     */
    @Resubmit(spEL = "'updateMultiDictStatus' + #userPo.userId", message = "修改级联字典状态")
    @PostMapping("/update/status")
    @Auth(permit = "multi:dict:update")
    public Result<Object> updateMultiDictStatus(@Validated @RequestBody UpdateStatusVo updateStatusVo) {
        log.info("开始修改级联字典状态业务。{}", updateStatusVo);

        List<Long> idList = updateStatusVo.getIdList();
        if (batchProperty.getMaxSize() < idList.size()) {
            log.warn("修改级联字典状态数量超限。batch.maxSize={} list.size={}", batchProperty.getMaxSize(), idList.size());
            return Result.clientFail("一次修改级联字典状态，最多允许同时修改" + batchProperty.getMaxSize() + "个字典");
        }

        multiDictService.updateMultiDictStatus(updateStatusVo);

        log.info("修改级联字典状态业务结束");
        return Result.success();
    }

    /**
     * 删除级联字典接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-级联字典管理页面调用
     * <p>
     * 此接口会校验是否重复提交
     * @param idList 主键列表
     * @return 结果
     */
    @Resubmit(spEL = "'deleteMultiDict' + #userPo.userId", message = "删除级联字典")
    @DeleteMapping("/delete")
    @Auth(permit = "multi:dict:delete")
    public Result<Object> deleteMultiDict(@RequestBody List<Long> idList) {
        log.info("开始删除级联字典业务。idList={}", idList);

        if (batchProperty.getMaxSize() < idList.size()) {
            log.warn("删除级联字典数量超限。batch.maxSize={} list.size={}", batchProperty.getMaxSize(), idList.size());
            return Result.clientFail("一次删除级联字典状态，最多允许同时删除" + batchProperty.getMaxSize() + "个字典");
        }

        multiDictManager.deleteMultiDict(idList);

        log.info("删除级联字典业务结束");
        return Result.success();
    }

}
